﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace CompactMapper.Orm.Enumerations
{
    public abstract class Enumeration
    {
    }


    public abstract class Enumeration<TValue> : Enumeration
    {
        private readonly TValue _value;

        protected Enumeration(TValue value)
        {
            _value = value;
        }

        public TValue Value
        {
            get { return _value; }
        }

        protected static IEnumerable<T> GetAll<T>() where T : Enumeration<TValue>
        {
            Type type = typeof (T);
            FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);

            foreach (FieldInfo info in fields)
            {
                var locatedValue = info.GetValue(null) as T;

                if (locatedValue != null)
                {
                    yield return locatedValue;
                }
            }
        }

        public override bool Equals(object obj)
        {
            var otherValue = obj as Enumeration<TValue>;

            if (otherValue == null)
            {
                return false;
            }

            bool typeMatches = GetType().Equals(obj.GetType());
            bool valueMatches = _value.Equals(otherValue.Value);

            return typeMatches && valueMatches;
        }

        public override int GetHashCode()
        {
            return _value.GetHashCode();
        }

        public static T FromValue<T>(TValue value) where T : Enumeration<TValue>
        {
            T matchingItem = Parse<T, TValue>(value, "value", item => item.Value.Equals(value));
            return matchingItem;
        }

        private static T Parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration<TValue>
        {
            T matchingItem = GetAll<T>().FirstOrDefault(predicate);

            if (matchingItem == null)
            {
                string message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof (T));
                throw new ApplicationException(message);
            }

            return matchingItem;
        }
    }
}